package concurrency;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by sunzhengjie on 2017/1/20.
 */
public class RestaurantLock {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    Meal meal;
    ExecutorService exec = Executors.newCachedThreadPool();
    final Chef chef = new Chef(this);
    final WaitPerson waitPerson = new WaitPerson(this);//如果不是final引用的对象可能被改变，不同线程获取waitPerson对象锁可能不同。

    public RestaurantLock() {
        exec.execute(chef);
        exec.execute(waitPerson);
    }

    public static class Meal {
        private final int orderNum;

        public Meal(int orderNum) {
            this.orderNum = orderNum;
        }

        public String toString() {
            return "Meal " + orderNum;
        }
    }

    public static class Chef implements Runnable {
        RestaurantLock restaurant;
        int count = 0;

        public Chef(RestaurantLock restaurant) {
            this.restaurant = restaurant;
        }

        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    restaurant.lock.lock();
                    while (restaurant.meal != null) {
                        restaurant.condition.await();
                    }
                    if (++count == 10) {
                        System.out.println("restaurant closing");
                        restaurant.exec.shutdownNow();
                    }
                    System.out.println("order up");
                    restaurant.meal = new Meal(count);
                    TimeUnit.SECONDS.sleep(1);
                    restaurant.condition.signalAll();
                }
            } catch (InterruptedException ex) {
                System.out.println("Chef interrupt");
            } finally {
                restaurant.lock.unlock();
            }
        }
    }

    public static class WaitPerson implements Runnable {
        RestaurantLock restaurant;

        public WaitPerson(RestaurantLock restaurant) {
            this.restaurant = restaurant;
        }

        @Override
        public void run() {
            try {
                while (!Thread.interrupted()) {
                    restaurant.lock.lock();
                    while (restaurant.meal == null) {
                        restaurant.condition.await();
                    }
                    System.out.println("WaitPerson got " + restaurant.meal);
                    restaurant.meal = null;
                    TimeUnit.SECONDS.sleep(1);
                    restaurant.condition.signalAll();
                }
            } catch (InterruptedException ex) {
                System.out.println("WaitPerson interrupt");
            } finally {
                restaurant.lock.unlock();
            }
        }
    }

    public static void main(String[] arg0) {
        new Restaurant();
    }

}
